﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LcfContols
{
    /// <summary>
    /// 三态树控件类，此类继承于TreeViewDrag
    /// </summary>
    [Serializable()]
    public class LTreeView : System.Windows.Forms.TreeView
    {
        #region "构造函数与析构函数"
        /// <summary>
        /// 初始化 ThreeStateIconTreeView 类的新实例。
        /// </summary>
        public LTreeView()
        {
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LTreeView));
            this.imgCheckBoxState = new System.Windows.Forms.ImageList(this.components);
            this.imgRadioBoxState = new System.Windows.Forms.ImageList(this.components);
            this.SuspendLayout();
            // 
            // imgCheckBoxState
            // 
            this.imgCheckBoxState.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imgCheckBoxState.ImageStream")));
            this.imgCheckBoxState.TransparentColor = System.Drawing.Color.Transparent;
            this.imgCheckBoxState.Images.SetKeyName(0, "checkbox.png");
            this.imgCheckBoxState.Images.SetKeyName(1, "checkbox_unchecked.png");
            this.imgCheckBoxState.Images.SetKeyName(2, "checkbox_checked.png");
            this.imgCheckBoxState.Images.SetKeyName(3, "checkbox.png");
            this.imgCheckBoxState.Images.SetKeyName(4, "checkbox.png");
            this.imgCheckBoxState.Images.SetKeyName(5, "checkbox.png");
            // 
            // imgRadioBoxState
            // 
            this.imgRadioBoxState.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
            this.imgRadioBoxState.ImageSize = new System.Drawing.Size(13, 13);
            this.imgRadioBoxState.TransparentColor = System.Drawing.Color.Transparent;
            // 
            // LTreeViewTest
            // 
            this.LineColor = System.Drawing.Color.Black;
            this.ResumeLayout(false);

        }

        /// <summary>
        /// 初始化 ThreeStateIconTreeView 类的新实例。
        /// </summary>
        public LTreeView(System.ComponentModel.IContainer container)
            : this()
        {
            container.Add(this);
            InitializeComponent();
        }

        /// <summary>
        /// 释放资源。
        /// </summary>
        ~LTreeView()
        {
            this.Dispose();
        }
        #endregion

        #region "内部变量"

        private bool m_bReadOnly;
        private bool m_bOnlyLeavesCheckBox;
        private bool m_bThreeState = true;
        //private ImageList m_imgListState=null;

        private EnumTreeViewType m_eTreeViewType = EnumTreeViewType.MultiSelect;
        private bool m_bRadioCheck;
        private EnumRadioCheckType m_eRadioCheckType = EnumRadioCheckType.TreeView;
        private TreeNode m_nodeRadioCheckedParent; //当m_eRadioCheckType=EnumRadioCheckType.TreeView，记录被选中的项

        private bool m_bSelectNodeAtRButtonMouseDownOneNode = true;

        internal System.Windows.Forms.ImageList imgCheckBoxState;
        internal System.Windows.Forms.ImageList imgRadioBoxState;
        private System.ComponentModel.IContainer components;
        #endregion

        #region "属性"
        /// <summary>
        /// 只读属性，如果为True，则用户不能在界面上修改节点的CheckBox状态，<br></br>
        /// 如果为False，则用户可以通过鼠标或键盘修改节点的CheckBox状态。
        /// </summary>
        //***********************************************************
        //*     属性名称：readonly
        //*     功能描述：只读属性
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        [Category("自定义属性")]
        [Description("只读属性"), DefaultValue(false)]
        public bool ReadOnly
        {
            get
            {
                return m_bReadOnly;
            }
            set
            {
                m_bReadOnly = value;
            }
        }

        /// <summary>
        /// 只有叶子打勾属性，如果为True，则只有叶子节点又CheckBox，其他节点没有<br></br>
        /// 如果为False，则所有节点都有CheckBox。
        /// </summary>
        //***********************************************************
        //*     属性名称：OnlyLeavesCheckBox
        //*     功能描述：只有叶子打勾属性
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        [Category("自定义属性")]
        [Description("决定是否只有叶子有CheckBox"), DefaultValue(false)]
        public bool OnlyLeavesCheckBox
        {
            get { return m_bOnlyLeavesCheckBox; }
            set
            {
                if (m_bOnlyLeavesCheckBox == value)
                    return;

                m_bOnlyLeavesCheckBox = value;
                Refresh();
            }
        }


        /// <summary>
        /// 决定节点有无CheckBox。
        /// </summary>
        //***********************************************************
        //*     属性名称：CheckBoxes
        //*     功能描述：重写CheckBoxes属性
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public new bool CheckBoxes
        {
            get
            {
                return base.CheckBoxes;
            }
            set
            {
                base.CheckBoxes = value;
                Refresh();
            }
        }

        /// <summary>
        /// 节点的图像列表。
        /// </summary>
        //***********************************************************
        //*     属性名称：ImageList
        //*     功能描述：重写ImageList属性
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public new ImageList ImageList
        {
            get
            {
                return base.ImageList;
            }
            set
            {
                base.ImageList = value;
                Refresh();
            }
        }
        /// <summary>
        /// 树类型。
        /// </summary>
        [Browsable(true)]
        [Category("自定义属性")]
        [Description("下拉框显示的树控件类型")]
        [DefaultValue(EnumTreeViewType.Normal)]
        public EnumTreeViewType TreeViewType
        {
            get { return m_eTreeViewType; }
            set
            {
                if (m_eTreeViewType == value)
                    return;

                m_eTreeViewType = value;
                switch (m_eTreeViewType)
                {
                    case EnumTreeViewType.Normal:
                        base.CheckBoxes = false;
                        break;
                    case EnumTreeViewType.MultiSelectNormal:
                        base.CheckBoxes = true;
                        m_bThreeState = false;
                        m_bRadioCheck = false;
                        break;
                    case EnumTreeViewType.MultiSelect:
                        base.CheckBoxes = true;
                        m_bThreeState = true;
                        m_bRadioCheck = false;
                        break;
                    case EnumTreeViewType.OneSelectNormal:
                        base.CheckBoxes = true;
                        m_bRadioCheck = true;
                        m_eRadioCheckType = EnumRadioCheckType.TreeView;
                        break;
                    case EnumTreeViewType.OneSelectInParent:
                        base.CheckBoxes = true;
                        m_bRadioCheck = true;
                        m_eRadioCheckType = EnumRadioCheckType.TreeNode;
                        break;
                }
                Refresh();
            }
        }


        /// <summary>
        /// 鼠标右键单击节点时，是否立即选中该节点。
        /// </summary>
        //***********************************************************
        //*     属性名称：SelectNodeAtRButtonMouseDownOneNode
        //*     功能描述：鼠标右键单击节点时，是否立即选中该节点
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        [Category("自定义属性")]
        [DefaultValue(true)]
        [Description("决定鼠标右键MouseDown一个节点时，是否选中该节点！")]
        public bool SelectNodeAtRButtonMouseDownOneNode
        {
            get { return m_bSelectNodeAtRButtonMouseDownOneNode; }
            set { m_bSelectNodeAtRButtonMouseDownOneNode = value; }
        }

        /// <summary>
        /// 是否是三态树属性，如果True，则节点CheckBox状态改变，其父节点和子节点状态会相应改变，<br></br>
        /// 如果false，则节点CheckBox状态改变，其父节点和子节点状态不会改变！
        /// </summary>
        //***********************************************************
        //*     属性名称：ThreeState
        //*     功能描述：是否三态树属性
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        [Category("自定义属性")]
        [DefaultValue(true)]
        [Description("是否是三态树属性，如果false，则节点CheckBox状态改变，其父节点和子节点状态不会改变！")]
        internal bool ThreeState
        {
            get { return m_bThreeState; }
            set { m_bThreeState = value; }
        }


        //***********************************************************
        //*     属性名称：RadioCheck
        //*     功能描述：决定是单选还是多选。
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        [Category("自定义属性")]
        [Description("只能单选属性"), DefaultValue(false)]
        internal bool RadioCheck
        {
            get
            {
                return m_bRadioCheck;
            }
            set
            {
                if (m_bRadioCheck == value)
                    return;

                m_bRadioCheck = value;
                Refresh();
            }
        }

        #endregion

        #region "Win-API 相关的结构和方法"
        [StructLayout(LayoutKind.Sequential)]
        private struct TVITEM
        {
            public int mask;
            public IntPtr hItem;
            public int state;
            public int stateMask;
            public int pszText;
            public int cchTextMax;
            public int iImage;
            public int iSelectedImage;
            public int cChildren;
            public int lParam;
        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        internal struct TV_HITTESTINFO
        {
            public Point pt;       //点结构;
            public TVHit flags;    //标志;
            public IntPtr hItem;
        };

        [Flags]
        internal enum TVHit
        {
            NoWhere = 0x1,
            OnItemIcon = 0x2,
            OnItemLabel = 0x4,
            OnItemIndent = 0x8,
            OnItemButton = 0x10,
            OnItemRight = 0x20,
            OnItemStateIcon = 0x40,
            OnItem = (OnItemIcon | OnItemLabel | OnItemStateIcon),
            Above = 0x100,
            Below = 0x200,
            ToRight = 0x400,
            ToLeft = 0x800,
            TVIS_STATEIMAGEMASK = 0xF000
        };

        internal enum TreeViewMessages         //消息
        {
            TV_FIRST = 0x1100,
            TVM_SETIMAGELIST = TV_FIRST + 9,
            TVM_GETITEM = TV_FIRST + 12,
            TVM_SETITEM = TV_FIRST + 13,
            TVM_HITTEST = TV_FIRST + 17
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TV_HITTESTINFO lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TVITEM lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, IntPtr lParam);
        #endregion

        #region "内部方法"
        //***********************************************************
        //*     函数名称：CheckNode
        //*     功能描述：设置节点状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        private void CheckNode(TreeNode node, EnumCheckState state)
        {
            BeginUpdate();
            SetInternalChecked(node, state);

            if (m_bThreeState && !m_bRadioCheck)
            {
                foreach (TreeNode child in node.Nodes)
                {
                    CheckNode(child, state);
                }
            }//

            if (m_bRadioCheck && state == EnumCheckState.Checked)
            {
                if (m_eRadioCheckType == EnumRadioCheckType.TreeView)
                {
                    SetChildNodeUnChecked(m_nodeRadioCheckedParent, node);
                    m_nodeRadioCheckedParent = node.Parent;
                }
                else
                {
                    SetChildNodeUnChecked(node.Parent, node);
                }

            }
            EndUpdate();
        }

        //***********************************************************
        //*     函数名称：ChangeParent
        //*     功能描述：改变父节点状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        private void ChangeParent(TreeNode node)
        {
            if (node == null)
            {
                return;
            }
            if (GetChecked(node) == EnumCheckState.None)
            {
                return;
            }

            EnumCheckState state = GetChecked(node.FirstNode);

            foreach (TreeNode child in node.Nodes)
            {
                if (GetChecked(child) == EnumCheckState.Indeterminate || GetChecked(child) != state)
                {
                    state = EnumCheckState.Indeterminate;
                    break;
                }
            } //

            SetInternalChecked(node, state);
            ChangeParent(node.Parent);
        }

        //***********************************************************
        //*     函数名称：ChangeNodeState
        //*     功能描述：改变节点状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        private void ChangeNodeState(TreeNode node)
        {
            EnumCheckState state = GetChecked(node);

            BeginUpdate();
            switch (state)
            {
                case EnumCheckState.Checked:
                    CheckNode(node, EnumCheckState.Unchecked);
                    break;
                case EnumCheckState.Indeterminate:
                    CheckNode(node, EnumCheckState.Indeterminate);
                    break;
                case EnumCheckState.Unchecked:
                    CheckNode(node, EnumCheckState.Checked);
                    break;
            }

            if (m_bThreeState && !m_bRadioCheck)
            {
                ChangeParent(node.Parent);
            }
            EndUpdate();
        }
        
        private TreeNode GetTreeNodeHitAtCheckBoxByClientPosition(int x, int y)
        {

            TreeNode node = null;

            int iTreeNodeHandle;
            TV_HITTESTINFO tTVHITTESTINFO = new TV_HITTESTINFO();

            tTVHITTESTINFO.pt.X = x;
            tTVHITTESTINFO.pt.Y = y;
            iTreeNodeHandle = SendMessage(this.Handle, TreeViewMessages.TVM_HITTEST, 0, ref tTVHITTESTINFO);

            if (iTreeNodeHandle != 0)
            {
                if ((tTVHITTESTINFO.flags & TVHit.OnItemStateIcon) != 0)
                {

                    node = TreeNode.FromHandle(this, new IntPtr(iTreeNodeHandle));

                }

            }
            return node;
        }

        private void SetParentCheckBoxStateNone(TreeNodeCollection Nodes)
        {
            foreach (TreeNode node in Nodes)
            {
                if (node.Nodes.Count > 0)
                {
                    SetInternalChecked(node, EnumCheckState.None);
                    SetParentCheckBoxStateNone(node.Nodes);
                }
            }
        }

        private EnumCheckState SetParentCheckBoxStateNotNone(TreeNode Node)
        {
            EnumCheckState stateRet = EnumCheckState.None;
            if (Node.Nodes[0].Nodes.Count > 0)
            {
                stateRet = SetParentCheckBoxStateNotNone(Node.Nodes[0]);
            }
            else
            {
                stateRet = GetChecked(Node.Nodes[0]);
            }

            for (int i = 1; i < Node.Nodes.Count; i++)
            {
                EnumCheckState state;
                if (Node.Nodes[i].Nodes.Count > 0)
                {
                    state = SetParentCheckBoxStateNotNone(Node.Nodes[i]);
                }
                else
                {
                    state = GetChecked(Node.Nodes[i]);
                }

                if (stateRet != state)
                {
                    stateRet = EnumCheckState.Indeterminate;
                    break;
                }
            }

            SetInternalChecked(Node, stateRet);

            return stateRet;
        }

        private void SetChildNodeUnChecked(TreeNode node, TreeNode butThisNodeUnChanged)
        {
            TreeNodeCollection Nodes;
            if (node == null)
                Nodes = this.Nodes;
            else
                Nodes = node.Nodes;

            foreach (TreeNode nod in Nodes)
            {
                if (nod == butThisNodeUnChanged)
                    continue;

                if (GetChecked(nod) == EnumCheckState.Checked)
                {
                    SetInternalChecked(nod, EnumCheckState.Unchecked);
                    return;
                }
            }

        }
        #endregion

        #region "外部方法"
        /// <summary>
        /// 设置状态图标列表。
        /// </summary>
        /// <param name="ImgListState">
        /// 状态图标列表
        /// </param>
        //***********************************************************
        //*     函数名称：SetStateImageList
        //*     功能描述：设置状态图标的ImageList
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public void SetStateImageList(ImageList ImgListState)
        {
            if (this.CheckBoxes == true)
            {
                if (ImgListState != null)
                {
                    SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, ImgListState.Handle);
                }
                else
                {
                    //SendMessage(this.Handle,TreeViewMessages.TVM_SETIMAGELIST,2,IntPtr.Zero);
                    if (!m_bRadioCheck)
                    {
                        SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, this.imgCheckBoxState.Handle);
                    }
                    else
                    {
                        SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, this.imgRadioBoxState.Handle);
                    }
                }
            }
        }

        /// <summary>
        /// 获取某个节点的状态。
        /// </summary>
        /// <param name="node">要获取状态的节点</param>
        /// <returns>当前节点的状态</returns>
        //***********************************************************
        //*     函数名称：GetChecked
        //*     功能描述：获取当前节点的状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public EnumCheckState GetChecked(TreeNode node)
        {
            TVITEM tTVITEM = new TVITEM();
            int iState;
            int iResult;
            tTVITEM.mask = (int)(TVHit.OnItemButton | TVHit.OnItemIndent);
            tTVITEM.hItem = node.Handle;
            tTVITEM.stateMask = (int)(TVHit.TVIS_STATEIMAGEMASK);
            tTVITEM.state = 0;

            iResult = SendMessage(this.Handle, TreeViewMessages.TVM_GETITEM, 0, ref tTVITEM);
            if (iResult != 0)
            {
                iState = tTVITEM.state;
                iState = iState / 0xFFF;

                return (EnumCheckState)(iState);
            }
            else
            {
                return EnumCheckState.Unchecked;
            }
        }

        /// <summary>
        /// 设置节点状态。
        /// </summary>
        /// <param name="node">要设置的节点</param>
        /// <param name="state">设置的状态</param>
        //***********************************************************
        //*     函数名称：SetChecked
        //*     功能描述：设置节点的状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public void SetChecked(TreeNode node, EnumCheckState state)
        {
            CheckNode(node, state);
            if (m_bThreeState && !m_bRadioCheck)
            {
                ChangeParent(node.Parent);
            }
        }

        /// <summary>
        /// 设置一个节点的状态，且当前节点的父节点和子节点状态不改变。
        /// </summary>
        /// <param name="node">要设置的节点</param>
        /// <param name="state">设置的状态</param>
        //***********************************************************
        //*     函数名称：SetInternalChecked
        //*     功能描述：改变当前节点的状态
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public void SetInternalChecked(TreeNode node, EnumCheckState state)
        {
            int iState = (int)state;

            TVITEM tTVITEM = new TVITEM();
            tTVITEM.mask = (int)(TVHit.OnItemButton | TVHit.OnItemIndent);
            tTVITEM.hItem = node.Handle;
            tTVITEM.stateMask = (int)TVHit.TVIS_STATEIMAGEMASK;
            tTVITEM.state = iState * 0x1000;

            SendMessage(this.Handle, TreeViewMessages.TVM_SETITEM, 0, ref tTVITEM);

            base.OnAfterCheck(new System.Windows.Forms.TreeViewEventArgs(node));
        }
        
        #endregion

        #region "重写方法"
        /// <summary>
        /// 重写控件的 Refresh 方法，重绘控件
        /// </summary>
        //***********************************************************
        //*     函数名称：Refresh
        //*     功能描述：重写Refresh方法
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        public override void Refresh()
        {
            SetStateImageList(null);

            if (m_bOnlyLeavesCheckBox && this.Nodes.Count > 0)
            {
                SetParentCheckBoxStateNone(this.Nodes);
            }
            else
            {
                //				foreach(TreeNode node in this.Nodes)
                //				{
                //					if(node.Nodes.Count>0)
                //						SetParentCheckBoxStateNotNone(node);
                //				}
            }
            base.Refresh();
        }

        /// <summary>
        /// 重写控件的 OnParentChanged 方法，重绘控件
        /// </summary>
        //***********************************************************
        //*     函数名称：OnParentChanged
        //*     功能描述：重写OnParentChanged方法
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        protected override void OnParentChanged(EventArgs e)
        {
            Refresh();
            base.OnParentChanged(e);
        }

        /// <summary>
        /// 重写控件的 OnClick 方法，判断鼠标位置是否在状态图标内，如果在，则改变节点状态。
        /// </summary>
        //***********************************************************
        //*     函数名称：OnClick
        //*     功能描述：重写单击事件，判断鼠标单击位置
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        protected override void OnClick(System.EventArgs e)
        {
            Point pt = PointToClient(Control.MousePosition);
            base.OnClick(e);
            if (m_bReadOnly || m_bCancel)
            {
                return;
            }

            TreeNode node = GetTreeNodeHitAtCheckBoxByClientPosition(pt.X, pt.Y);

            if (node != null)
            {
                ChangeNodeState(node);
            }

        }

        /// <summary>
        /// 重写控件的 OnVisibleChanged 方法，重绘控件
        /// </summary>
        protected override void OnVisibleChanged(EventArgs e)
        {
            if (this.Visible)
                Refresh();
            base.OnVisibleChanged(e);
        }

        /// <summary>
        /// 重写控件的 OnMouseDown 方法，鼠标右键选中节点
        /// </summary>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right && m_bSelectNodeAtRButtonMouseDownOneNode)
            {
                TreeNode node = this.GetNodeAt(e.X, e.Y);
                if (node != null)
                    this.SelectedNode = node;
            }
            base.OnMouseDown(e);
        }
        /// <summary>
        /// 重写控件的 OnKeyDown 方法，当用户按下空格键，改变节点状态。
        /// </summary>
        //***********************************************************
        //*     函数名称：OnKeyDown
        //*     功能描述：重写KeyDown事件，判断按下的是否是空格键
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if (e.KeyCode == Keys.Space)
            {
                if (m_bReadOnly || m_bCancel)
                {
                    return;
                }

                if (SelectedNode != null)
                {
                    ChangeNodeState(SelectedNode);
                }
            }
        }

        /// <summary>
        /// 重写控件的 OnBeforeCheck 方法，取消Check。
        /// </summary>
        //***********************************************************
        //*     函数名称：OnBeforeCheck
        //*     功能描述：重写OnBeforeCheck事件，取消Check
        //*     参    数：无
        //*     返 回 值：
        //*     作    者：
        //*     创建日期：2004-06-28
        //***********************************************************
        protected override void OnBeforeCheck(System.Windows.Forms.TreeViewCancelEventArgs e)
        {
            if (m_bReadOnly == true)
            {
                e.Cancel = true;
                m_bCancel = e.Cancel;
                return;
            }

            //>>防止单选按钮取消
            if (m_bRadioCheck && GetChecked(e.Node) == EnumCheckState.Checked)
            {
                e.Cancel = true;
                m_bCancel = e.Cancel;
                return;
            }
            //<<防止单选按钮取消

            base.OnBeforeCheck(e);

            if (e.Cancel == false)
                ChangeNodeState(e.Node);

            m_bCancel = e.Cancel;
        }

        private bool m_bCancel = false;
        #endregion

    }

    #region "EnumTreeViewType --- 树类型"
    /// <summary>
    /// 树类型枚举。
    /// </summary>
    public enum EnumTreeViewType
    {
        /// <summary>
        /// 标准树，当前树和<see cref="System.Windows.Forms.TreeView">System.Windows.Forms.TreeView</see> 类似
        /// </summary>
        Normal = 0,
        /// <summary>
        /// 多选一般树，可以多选，但不支持三态
        /// </summary>
        MultiSelectNormal = 1,
        /// <summary>
        /// 多选三态树
        /// </summary>
        MultiSelect = 2,
        /// <summary>
        /// 单选一般树，整个树最多只能选中一个节点
        /// </summary>
        OneSelectNormal = 3,
        /// <summary>
        /// 单选同一父节点树，整个树可以选中多个节点，但同一父节点下，最多只能选中一个子节点
        /// </summary>
        OneSelectInParent = 4
    }
    #endregion

    #region "EnumCheckState  --- CheckBox状态"
    /// <summary>
    /// 节点状态类型枚举
    /// </summary>
    public enum EnumCheckState
    {

        /// <summary>
        /// 没有复选框
        /// </summary>
        None = 0,             //没有复选框
        /// <summary>
        /// 复选框为不选中状态
        /// </summary>
        Unchecked = 1,        //复选框为没有选中状态
        /// <summary>
        /// 复选框为选中状态
        /// </summary>
        Checked = 2,          //复选框为选中状态
        /// <summary>
        /// 复选框为中间状态
        /// </summary>
        Indeterminate = 3     //复选框为半选中状态
    }
    #endregion

    #region "EnumRadioCheckType   --- RadioCheck类型"
    /// <summary>
    /// 单选树的单选类型
    /// </summary>
    public enum EnumRadioCheckType
    {
        /// <summary>
        /// 整个树最多只能有一个节点被选中
        /// </summary>
        TreeView = 0,

        /// <summary>
        /// 整个树可以有多个节点被选中，但同一父节点下，最多只能有一个子节点被选中
        /// </summary>
        TreeNode = 1
    }
    #endregion
}
